<template>
  <div>
    <!-- 19 增加全屏按钮 -->
    <!-- 20 用控制面板控制全屏
       <button class="full-screen-btn" @click="onClickFullScreen">全屏</button> 
      -->
  </div>
</template>

<script>
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 20 导入gui控制面板
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";

// 导入HDR加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";

// GLTFLoader
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

// 24.1 导入顶点法向量辅助器
import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper";

/**
 * 值类型
 * - Vector3
 * (x,y,z)
 * - Euler 欧拉: 描述一个旋转变换,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体
 * (x,y,z,order) - order: 'XYZ' or 'YXZ' or 'ZXY' or 'ZYX',
 */

export default {
  data() {
    return {};
  },
  methods: {
    // 19 增加全屏按钮
    onClickFullScreen() {
      // 渲染器画布全屏
      window.renderer && window.renderer.domElement.requestFullscreen();
      // 浏览器窗口全屏
      // document.body.requestFullscreen()
      // 退出全屏
      // document.exitFullscreen()
    },
  },
  mounted() {
    // 1 创建场景
    const scene = new THREE.Scene();

    // 2 创建相机
    const camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000,
    );
    camera.position.z = 5;
    camera.position.x = 5;
    camera.position.y = 5;
    // 3 相机指向原点
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    // 4 创建一个渲染器 canvas
    const renderer = new THREE.WebGLRenderer({
      // 4.1.1 设置渲染器锯齿属性
      antialias: true,
    });
    // 4.1.2 属性设置
    // renderer.antialias = true
    renderer.setSize(window.innerWidth, window.innerHeight);

    document.body.appendChild(renderer.domElement);

    // 8 添加轨道控制器
    const controls = new OrbitControls(camera, renderer.domElement); // 创建轨道控制器, 第二参数表示监听事件的DOM
    // const controls = new OrbitControls(camera, document.body); // 创建轨道控制器, 第二参数表示监听事件的DOM, 监听了body后, OrbitControls会拦截所有的事件
    // 9 设置阻尼
    controls.enableDamping = true;
    controls.dampingFactor = 0.1; // 设置阻尼系数, 数值越小, 惯性越大, 数值越大, 停下来越快

    // 10 自动旋转
    controls.autoRotate = false; // 自动旋转

    // 7 添加世界坐标辅助器 x轴向右红色r y轴向上绿色g z轴向前蓝b
    const axesHelper = new THREE.AxesHelper(5); // 辅助线长度为5
    scene.add(axesHelper);

    // 6 动作绘制
    function animate() {
      /* 8 添加轨道控制器 根据控制器更新画面 */
      controls.update();
      requestAnimationFrame(animate); // 请求浏览器重新绘制画面
      /* 自动旋转
      cube.rotation.x += 0.01; // x轴旋转速度增加
      cube.rotation.y += 0.01; // y轴旋转速度增加
      */
      renderer.render(scene, camera); // 渲染场景和相机设置到屏幕上
    }
    animate(); // 开始动画循环

    // 18 监听全屏事件, 让画布适配屏幕尺寸
    window.addEventListener("resize", function() {
      // 重置渲染器宽高比
      renderer.setSize(window.innerWidth, window.innerHeight);
      // 4.2 设置设备像素比
      renderer.setPixelRatio(window.devicePixelRatio);
      // 重置相机宽高比
      camera.aspect = window.innerWidth / window.innerHeight;
      // 更新相机投影矩阵
      camera.updateProjectionMatrix();
    });
    /** 20 用GUI控制面板控制全屏
    // 19 增加全屏按钮
    window.scene = scene;
    window.camera = camera;
    window.renderer = renderer;
    window.controls = controls;
    */
    // 20 GUI控制面板
    let eventObj = {
      Fullscreen: function() {
        document.body.requestFullscreen();
      },
      ExitFullscreen: function() {
        document.exitFullscreen();
      },
    };
    const gui = new GUI();
    // 第一个参数是事件对象, 第二个参数是该事件对象的key
    gui.add(eventObj, "Fullscreen").name("全屏");
    gui.add(eventObj, "ExitFullscreen").name("退出全屏");

    // 加载材质
    const textureLoader = new THREE.TextureLoader();
    const texture = textureLoader.load("/imgs/img_5.png");

    // 使用PlaneGeometry创建平面
    const planeGeometry = new THREE.PlaneGeometry(1, 1.4);
    const planeMaterial = new THREE.MeshBasicMaterial({
      side: THREE.DoubleSide,
      map: texture,
      combine: THREE.MixOperation,
      // 27.1 THREE.MixOperation 反射率
      reflectivity: 1, // 设置反射强度
    });
    const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
    planeMesh.position.x = -3;
    // PlaneGeometry自带uv属性
    scene.add(planeMesh);

    // 使用顶点创建平面
    const customGeometry = new THREE.BufferGeometry();
    const customVertices = new Float32Array([
      0,
      0,
      0,
      1,
      0,
      0,
      1,
      1.4,
      0,
      0,
      1.4,
      0,
    ]);
    // 21 给自定义结合设置uv配置
    // 21.1 设置uv坐标 整张图片边缘取值区间为[0,1]
    const uvAxis = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
    customGeometry.setAttribute("uv", new THREE.BufferAttribute(uvAxis, 2));
    // 23.1 让结合体计算法向量 - 似乎不生效
    // customGeometry.computeVertexNormals();
    // 23.2 手动设置法向量 normal 属性
    const customNormals = new Float32Array([
      0,
      0,
      1,
      0,
      0,
      1,
      0,
      0,
      1,
      0,
      0,
      1,
    ]);
    customGeometry.setAttribute(
      "normal",
      new THREE.BufferAttribute(customNormals, 3),
    );
    customGeometry.setAttribute(
      "position",
      new THREE.BufferAttribute(customVertices, 3),
    );
    const customIndices = new Uint16Array([0, 1, 2, 0, 2, 3]);
    customGeometry.setIndex(new THREE.BufferAttribute(customIndices, 1));
    const customMaterial = new THREE.MeshBasicMaterial({
      side: THREE.DoubleSide,
      map: texture,
      combine: THREE.MixOperation,
      // 27.1 THREE.MixOperation 反射率
      reflectivity: 1, // 设置反射强度
      transparent: true
    });

    // 28 移动几何体 几何体的position的数值会加上配置数值
    /**
     * 转换的两种方式
     * 1 对顶点数据的转换 - rotation和scale等属性都为0
     * 2 对物体几何体的转换 - rotation和scale等属性存在数值
     */
    customGeometry.translate(2, -0.5, 0)

    const customPlaneMesh = new THREE.Mesh(customGeometry, customMaterial);
    // customPlaneMesh.position.x = 2;
    // customPlaneMesh.position.y = -0.5;

    // BufferGeometry没有uv属性 => 21
    scene.add(customPlaneMesh);

    // 24 给物体添加法向量辅助器
    // 24.2 创建法向量辅助器
    const customPlaneMeshNormalVectorHelper = new VertexNormalsHelper(customPlaneMesh, 0.2, 0xff0000)
    scene.add(customPlaneMeshNormalVectorHelper)

    const rgbeLoader = new RGBELoader();
    rgbeLoader.load("/imgs/fullBG2.hdr", (envTexture) => {
      envTexture.mapping = THREE.EquirectangularReflectionMapping; // 设置映射类型
      scene.background = envTexture; // 设置背景
      scene.environment = envTexture; // 设置环境贴图
      planeMesh.environment = envTexture;
      planeMaterial.environment = envTexture;
      customPlaneMesh.environment = envTexture;
      customMaterial.environment = envTexture;
    });

    // // 22 添加平行光
    // const directionalLight = new THREE.DirectionalLight(0xffffff, 3.0);
    // directionalLight.position.set(80, 100, 50);
    // directionalLight.intensity = 5.0;
    // gui
    //   .add(directionalLight, "intensity") 
    //   .min(0)
    //   .max(10)
    //   .name("光强度");
    // scene.add(directionalLight);

    // 31 加载模型
    // 31.1 创建glft加载器 
    const gltfLoader = new GLTFLoader();
    // 31.2 加载模型 支持加载glb和gltf格式
    /* glb已经是二进制文件
       gltf还是json文本文件 */
    gltfLoader.load("/material/TV.glb", (gltf) => {
      gltf.scene.position.z = -6;
      gltf.scene.position.x = 3;
      gltf.scene.rotation.y = Math.PI / 2;
      gltf.scene.scale.set(0.2, 0.2, 0.2);
      // 31.3 将模型的场景添加到画布场景中
      scene.add(gltf.scene);

      // 29 将物体的包围盒添加到场景值中
      // 29.1 根据名字获取图形的实例
      const TVObject = gltf.scene.getObjectByName('TV_2_')
      // 29.1.1 如果该实例是多个Mesh组成的Group, 则取第一个
      console.log("Debugger ~ gltfLoader.load ~ TVObject:", TVObject)
      // 29.2 获取图形的几何
      const TVMesh = TVObject.geometry ? TVObject : TVObject.children[0]
      console.log("Debugger ~ gltfLoader.load ~ TVMesh:", TVMesh)
      const TVGeometry = TVMesh.geometry
      // 29.3 计算包围盒
      TVGeometry.computeBoundingBox()
      // 30 设置几何体居中
      TVGeometry.center()
      console.log("Debugger ~ gltfLoader.load ~ TVGeometry:", TVGeometry)
      // 29.4 获取包围盒 
      const TVBoundingBox = TVGeometry.boundingBox
      console.log("Debugger ~ gltfLoader.load ~ TVBoundingBox:", TVBoundingBox)
      // 29.4.1 更新材质的世界矩 - 计算材质变换后的数据
      TVMesh.updateWorldMatrix(true, true)
      // 29.4.2 更新包围盒 - 包围盒跟随变换
      TVBoundingBox.applyMatrix4(TVMesh.matrixWorld)
      // 31 获取包围盒的中心点
      let boxCenterVector3 = TVBoundingBox.getCenter(new THREE.Vector3())
      console.log("Debugger ~ gltfLoader.load ~ boxCenterVector3:", boxCenterVector3)
      // 29.5 创建包围盒辅助器
      const TVBoxHelper = new THREE.Box3Helper(TVBoundingBox, 0xff00ff)
      console.log("Debugger ~ gltfLoader.load ~ TVBoxHelper:", TVBoxHelper)
      // 29.6 添加包围盒辅助器到场景
      scene.add(TVBoxHelper)

      // 32 获取包围球 TVBoundingSphere: {center: Vector3, radius: float}
      const TVBoundingSphere = TVGeometry.boundingSphere
      console.log("Debugger ~ gltfLoader.load ~ TVBoundingSphere:", TVBoundingSphere)
      // 32.1 应用变换后的矩阵
      TVBoundingSphere.applyMatrix4(TVMesh.matrixWorld)
      // 32.2 创建包围球的辅助器 - 使用包围求的中心点和半径制造一个网状物
      // 32.2.1 创建几何体
      const TVBoundingSphereGeometry =  new THREE.SphereGeometry(TVBoundingSphere.radius, 16, 16)
      // 32.2.2 创建材质
      const TVBoundingSphereMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true})
      // 32.2.3 几何为网状物
      const TVBoundingSphereHelperMesh = new THREE.Mesh(TVBoundingSphereGeometry,TVBoundingSphereMaterial)
      // 32.2.4 设置位置 - 将包围球的中心店数据
      TVBoundingSphereHelperMesh.position.copy(TVBoundingSphere.center)
      // 32.2.5 将包围球辅助器添加到场景中
      scene.add(TVBoundingSphereHelperMesh)

    });
  },
};
</script>

<style>
* {
  margin: 0;
  padding: 0;
}
body {
  height: 100vh;
  width: 100vw;
  overflow: hidden;
}

.full-screen-btn {
  position: fixed;
  left: 2vw;
  top: 2vh;
  z-index: 2;
}
</style>
